/********************************************************************************
*                                                                               *
* kWookaExecuteProcess.hpp -- The execute process                               *
*                                                                               *
* Copyright (c) Fengren Technology(Guangzhou) Co.LTD. All rights reserved.      *
*                                                                               *
********************************************************************************/


#ifndef kWookaExecuteProcess_hpp
#define kWookaExecuteProcess_hpp

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#ifdef __WXMAC__
#include <sys/sysctl.h>
#include <libproc.h>
#else
#ifdef WIN32
#include <windows.h>
#include <Tlhelp32.h>
#include <psapi.h>
#endif
#endif

#include <wx/dynarray.h>
#include "wx/process.h"
#include "netinfo.h"

#define ID_THREAD_EVENT_LOG				7001
#define ID_THREAD_EVENT_PROCESS_END		7002

class kWookaProjectInfo;
class kWookaAppItemInfo;
class kWookaProcessExecutor;
class wxProcess;
class kWookaProcessPerformanceInfo;

class wxOutputStream;

WX_DECLARE_OBJARRAY(kWookaProcessExecutor*, kWookaProcessExecutorArray);

void BuildExecuteEnv(wxExecuteEnv& env, kWookaProjectInfo* proj, kWookaAppItemInfo* app);

wxString EnsureDirectorString(const wxString& dirstr);

class kWookaLogger {
public:
	kWookaLogger() {}
	~kWookaLogger() {
		logTexts.clear();
	}

public:
	wxArrayString logTexts;
};

class kWookaProcessOutputRender {
public:
	virtual void SendTextLog(const wxString& text) = 0;
};

class kWookaExecutorFactory : public wxObject {

public:
	virtual wxProcess* Execute(kWookaProjectInfo* WXUNUSED(proj), kWookaAppItemInfo* WXUNUSED(app)) {
		return NULL;
	}

	virtual wxProcess* Execute(kWookaProcessOutputRender* WXUNUSED(render), kWookaProjectInfo* proj, kWookaAppItemInfo* app) {
		return Execute(proj, app);
	}

	virtual wxString BuildCommandLine(kWookaProjectInfo* proj, kWookaAppItemInfo* app) = 0;

	virtual bool IsReady() {
		return false;
	}

	virtual bool IsMultiProcess() {
		return false;
	}

	virtual bool Prepared(kWookaProcessOutputRender* WXUNUSED(executor), kWookaProjectInfo* WXUNUSED(proj), kWookaAppItemInfo* WXUNUSED(app)) {
		return true;
	}

	virtual bool Shutdown(kWookaProcessOutputRender* WXUNUSED(executor), kWookaProjectInfo* WXUNUSED(proj), kWookaAppItemInfo* WXUNUSED(app)) {
		return true;
	}
};

class kWookaExecutorThread : public wxThread
{
private:
	kWookaProcessExecutorArray m_executors;
	boolean m_stop;
	wxCriticalSection criticalSection;

public:
	virtual wxThread::ExitCode Entry();
	virtual void StopThread();

	void AddExecutor(kWookaProcessExecutor* pExecutor);
	void DelExecutor(kWookaProcessExecutor* pExecutor);
	void DetachAll();
};



enum kWookaEnumProcessState
{
	Unknow = 0,
	NoStarted = 1,
	Starting = 2,
	Started  = 3,
	Ended = 4
};

class kWookaProcessExecutor: public wxEvtHandler, public kWookaProcessOutputRender
{
public:

	~kWookaProcessExecutor() {
		if (m_factory != NULL) {
			delete m_factory;
			m_factory = NULL;
		}
		m_performances.clear();
		if (m_proc != NULL) {
			m_proc->Detach();
			delete m_proc;
		}
		wxLogDebug(wxT("kWookaProcessExecutor destroyed."));
	}
public:
	void Create(kWookaProjectInfo* proj, kWookaAppItemInfo* app);
	void Start();
	wxString GetCommandLine();
	bool FetchConsoleOutput(int fetchesThenYied);
	void Attach(wxWindow* loggerWindow);
	void AttachListPanel(wxWindow* listPanel) {
		m_listPanel = listPanel;
	}
	void Detach();
	void Stop();
	bool IsStarted();
	bool CheckProcessReady();
	virtual void SendTextLog(const wxString& text) wxOVERRIDE;
	bool EvalProcessStatus(); /// check the process status wheather is started.
	void OnProcessEnd(wxProcessEvent& event);
	long GetPid() {
		return pid;
	}

	void PostExecuteStateChangedEvent(kWookaEnumProcessState oldPs = Unknow);

	void SetProcessFactory(kWookaExecutorFactory* factory) {
		if (m_factory != NULL) {
			delete m_factory;
			m_factory = NULL;
		}
		m_factory = factory;
	}

	kWookaProcessPerformanceInfo GetProcessPerformance();
	wxVector<kWookaProcessPerformanceInfo>& GetAllPerformances();

	wxArrayString& GetLogs() {
		return m_logs;
	}

	wxArrayString ReadLogs(int lines);

	void AddLogs(const wxString& cp);

	static void StartThread();
	static void StopThread();
	static DWORD GetProcessorNum();

	int GetProcessCpuPercent(const HANDLE hProcess, const DWORD dwElepsedTime);
	kWookaEnumProcessState GetExecuteState();
	
	/*
	 * if this parameter set to true, 
	 * we will check the process by the name
	 * because nginx. redis, mysql may fork to many processes.
	 */
	void SetMultiProcess(bool mp) {
		m_MultiProcess = mp;
	}

	void UpdateState(kWookaEnumProcessState us) {
		if (m_updatedState != us) {
			PostExecuteStateChangedEvent(us);
		}
		m_updatedState = us;
	}

	kWookaEnumProcessState GetUpdateState() {
		return m_updatedState;
	}

	
	bool Test(kWookaProcessOutputRender* render) {
		if (this->m_factory != NULL) {
			return this->m_factory->Prepared(render, m_project, m_appitem);
		}
		return true;
	}

	bool Test() {
		return Test(this);
	}

protected:
	kWookaProjectInfo* m_project; //Don't release
	kWookaAppItemInfo* m_appitem; //Don't release
	wxProcess* m_proc;		      //Release
	wxOutputStream* m_logoutput;  //Release
	kWookaExecutorFactory* m_factory; //Don't release
	long pid;
	bool m_alreadCheckedProcess;
	kWookaEnumProcessState m_updatedState;
	wxInputStream* m_ins;			// Don't release
	wxInputStream* m_err;			// Don't release
	wxArrayString  m_logs;			// Clear
	LARGE_INTEGER g_slgProcessTimeOld;
	static DWORD  dwProcessorCoreNum;
	wxVector<kWookaProcessPerformanceInfo> m_performances; // Clear
	wxVector<wxString> m_listenerUrls;
	bool m_started;
	// wxTextInputStream* m_tins;
	// WIN32
	HANDLE m_hProcess;
	bool m_ProcessInitialized;
	bool m_MultiProcess;
	LARGE_INTEGER m_lgPfreq;
	LARGE_INTEGER m_lgOldTime;
	IO_COUNTERS   m_ioOld;
	NetworkPerformanceItem m_netOld;
public:
	wxWindow* m_attachWindow;
	wxWindow* m_listPanel;
public:
	static kWookaExecutorThread* m_thread;
};


class kWookaSpringBootExecutorFactory : public kWookaExecutorFactory {
public:
	
	virtual bool Prepared(kWookaProcessOutputRender* WXUNUSED(executor), kWookaProjectInfo* WXUNUSED(proj), kWookaAppItemInfo* WXUNUSED(app)) wxOVERRIDE {
		return true;
	}

	virtual wxString BuildCommandLine(kWookaProjectInfo* proj, kWookaAppItemInfo* app);

	virtual wxProcess* Execute(kWookaProjectInfo* proj, kWookaAppItemInfo* app) wxOVERRIDE;
};

class kWookaMavenExecutorFactory : public kWookaExecutorFactory {
public:
	
	virtual bool Prepared(kWookaProcessOutputRender* executor, kWookaProjectInfo* proj, kWookaAppItemInfo* app) wxOVERRIDE;

	virtual wxString BuildCommandLine(kWookaProjectInfo* proj, kWookaAppItemInfo* app);

	virtual wxProcess* Execute(kWookaProjectInfo* proj, kWookaAppItemInfo* app) wxOVERRIDE;
};


class kWookaGenericJavaExecutorFactory : public kWookaExecutorFactory {
public:

	virtual bool Prepared(kWookaProcessOutputRender* WXUNUSED(executor), kWookaProjectInfo* WXUNUSED(proj), kWookaAppItemInfo* WXUNUSED(app)) wxOVERRIDE {
		return true;
	}

	virtual wxString BuildCommandLine(kWookaProjectInfo* proj, kWookaAppItemInfo* app);

	virtual wxProcess* Execute(kWookaProjectInfo* proj, kWookaAppItemInfo* app) wxOVERRIDE;
};

#endif /* kWookaExecuteProcess_hpp */
